package data

import (
	"fmt"

	"gorm.io/gorm"
)

type RbacSchema struct {
	db *DB
}

func NewRbacSchema(db *DB) (*RbacSchema, error) {
	return &RbacSchema{
		db: db,
	}, nil
}

/* 权限增删改查 */

// RbacPermitDelete 删除权限数据
func (s *RbacSchema) RbacPermitDelete(req []int64, dbx ...*gorm.DB) (int64, error) {
	return s.db.Delete(s.db.DB(dbx...), &RbacPermit{}, sqlIn("id", req))
}

type RbacPermitCreateReq struct {
	RbacPermit
}

// RbacPermitCreate 插入权限数据
func (s *RbacSchema) RbacPermitCreate(req []*RbacPermitCreateReq, dbx ...*gorm.DB) (int64, error) {
	return s.db.Insert(s.db.DB(dbx...), &RbacPermit{}, req, nil)
}

type RbacPermitUpdateReq struct {
	RbacPermit
}

// RbacPermitUpdate 更新权限数据
func (s *RbacSchema) RbacPermitUpdate(req []*RbacPermitUpdateReq, dbx ...*gorm.DB) error {
	table := &RbacPermit{}
	batch := func(tx *gorm.DB) error {
		for i, v := range req {
			if _, err := s.db.Update(tx, table, func(db *gorm.DB) *gorm.DB {
				db = sqlEqual("id", req[i].Id)(db)
				return db
			}, v); err != nil {
				return err
			}
		}
		return nil
	}
	return s.db.Transaction(s.db.DB(dbx...), batch)
}

// RbacPermitListReq 权限列表请求参数
type RbacPermitListReq struct {
	LimitOffset
}

// ListPermit 权限列表数据
func (s *RbacSchema) ListPermit(req *RbacPermitListReq) (total int64, lists []*RbacPermit, err error) {
	table := &RbacPermit{}
	where := func(db *gorm.DB) *gorm.DB {
		return db
	}
	orderLimit := func(db *gorm.DB) *gorm.DB {
		db = sqlDesc("id")(db)
		db = sqlLimitOffset(req.Limit, req.Offset)(db)
		return db
	}
	total, err = s.db.Table(table).Scopes(where).Count()
	if err != nil {
		return 0, nil, err
	}
	if total == 0 {
		lists = make([]*RbacPermit, 0)
		return total, lists, err
	}
	err = s.db.Table(table).Scopes(where, orderLimit).Find(&lists)
	return total, lists, err
}

/* 角色增删改查 */

// RbacRoleDelete 删除角色数据
func (s *RbacSchema) RbacRoleDelete(req []int64, dbx ...*gorm.DB) (int64, error) {
	return s.db.Delete(s.db.DB(dbx...), &RbacRole{}, sqlIn("id", req))
}

type RbacRoleCreateReq struct {
	RbacRole
}

// RbacRoleCreate 插入角色数据
func (s *RbacSchema) RbacRoleCreate(req []*RbacRoleCreateReq, dbx ...*gorm.DB) (int64, error) {
	return s.db.Insert(s.db.DB(dbx...), &RbacRole{}, req, nil)
}

type RbacRoleUpdateReq struct {
	RbacRole
}

// RbacRoleUpdate 更新角色数据
func (s *RbacSchema) RbacRoleUpdate(req []*RbacRoleUpdateReq, dbx ...*gorm.DB) error {
	table := &RbacRole{}
	batch := func(tx *gorm.DB) error {
		for i, v := range req {
			if _, err := s.db.Update(tx, table, func(db *gorm.DB) *gorm.DB {
				db = sqlEqual("id", req[i].Id)(db)
				return db
			}, v); err != nil {
				return err
			}
		}
		return nil
	}
	return s.db.Transaction(s.db.DB(dbx...), batch)
}

// RbacRoleListReq 角色列表请求参数
type RbacRoleListReq struct {
	LimitOffset
}

// ListRole 角色列表数据
func (s *RbacSchema) ListRole(req *RbacRoleListReq) (total int64, lists []*RbacRole, err error) {
	table := &RbacRole{}
	where := func(db *gorm.DB) *gorm.DB {
		return db
	}
	orderLimit := func(db *gorm.DB) *gorm.DB {
		db = sqlDesc("id")(db)
		db = sqlLimitOffset(req.Limit, req.Offset)(db)
		return db
	}
	total, err = s.db.Table(table).Scopes(where).Count()
	if err != nil {
		return 0, nil, err
	}
	if total == 0 {
		lists = make([]*RbacRole, 0)
		return total, lists, err
	}
	err = s.db.Table(table).Scopes(where, orderLimit).Find(&lists)
	return total, lists, err
}

/* 角色关联权限 */

type ListRolePermitReq struct {
	RoleId int64 `json:"role_id"`
}

type ListRolePermitRes struct {
	RoleId int64 `json:"role_id"`
	RbacPermit
	Children []*ListRolePermitRes
}

// ListRolePermit 角色的权限列表
func (s *RbacSchema) ListRolePermit(req *ListRolePermitReq) (lists []*ListRolePermitRes, err error) {
	db := s.db.DB()
	query := "WITH a AS ( SELECT * FROM %s WHERE ( role_id = ? ) SELECT a.role_id, b.* FROM a INNER JOIN %s b ON a.permit_id = b.id ORDER BY b.sort_number DESC, b.id DESC"
	query = fmt.Sprintf(query, RbacRolePermit{}.TableName(), RbacPermit{}.TableName())
	values := []any{req.RoleId}
	err = db.Raw(query, values...).Find(&lists).Error
	if err != nil {
		return lists, err
	}
	// TODO lists to tree?
	return lists, err
}

/* 角色关联用户 */

type ListRoleUserReq struct {
	RoleId int64 `json:"role_id"`
}

type ListRoleUserRes struct {
	RoleId int64 `json:"role_id"`
	User
}

// ListRoleUser 角色的用户列表
func (s *RbacSchema) ListRoleUser(req *ListRoleUserReq) (lists []*ListRoleUserRes, err error) {
	db := s.db.DB()
	query := "WITH a AS ( SELECT * FROM %s WHERE ( role_id = ? ) SELECT a.role_id, b.* FROM a INNER JOIN %s b ON a.user_id = b.id ORDER BY b.sort_number DESC, b.id DESC"
	query = fmt.Sprintf(query, RbacRoleUser{}.TableName(), User{}.TableName())
	values := []any{req.RoleId}
	err = db.Raw(query, values...).Find(&lists).Error
	if err != nil {
		return lists, err
	}
	// TODO lists to tree?
	return lists, err
}

type UpdateRoleReq struct {
	RoleId   int64   `json:"role_id"`
	PermitId []int64 `json:"permit_id"` // 权限id列表
	UserId   []int64 `json:"user_id"`   // 用户id列表
}

// UpdateRole 设置角色的 权限列表 或 用户列表 (允许同时设置)
func (s *RbacSchema) UpdateRole(req *UpdateRoleReq, dbx ...*gorm.DB) error {
	if req.RoleId <= 0 {
		return nil
	}
	var permit []*RbacRolePermit
	var user []*RbacRoleUser
	if req.PermitId != nil {
		req.PermitId = RemoveDuplicates(req.PermitId)
		permit = make([]*RbacRolePermit, 0, len(req.PermitId))
		for _, v := range req.PermitId {
			permit = append(permit, &RbacRolePermit{
				RoleId:   req.RoleId,
				PermitId: v,
			})
		}
	}
	if req.UserId != nil {
		req.UserId = RemoveDuplicates(req.UserId)
		user = make([]*RbacRoleUser, 0, len(req.PermitId))
		for _, v := range req.UserId {
			tmp := &RbacRoleUser{
				RoleId: req.RoleId,
			}
			tmp.UserId = v
			user = append(user, tmp)
		}
	}
	if permit != nil || user != nil {
		roleId := "role_id"
		batch := func(tx *gorm.DB) error {
			if permit != nil {
				rbacRolePermit := &RbacRolePermit{}
				_, err := s.db.Delete(tx, rbacRolePermit, sqlEqual(roleId, req.RoleId))
				if err != nil {
					return err
				}
				_, err = s.db.Insert(tx, rbacRolePermit, permit, nil)
				if err != nil {
					return err
				}
			}
			if user != nil {
				rbacRoleUser := &RbacRoleUser{}
				_, err := s.db.Delete(tx, rbacRoleUser, sqlEqual(roleId, req.RoleId))
				if err != nil {
					return err
				}
				_, err = s.db.Insert(tx, rbacRoleUser, user, nil)
				if err != nil {
					return err
				}
			}
			return nil
		}
		return s.db.Transaction(s.db.DB(dbx...), batch)
	}
	return nil
}
